package org.o14x.alpha.preferences;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Widget;
import org.o14x.alpha.domain.ExplorerTab;
import org.o14x.alpha.ui.controller.UIController;
import org.o14x.alpha.ui.views.folder.columns.ColumnHelper;
import org.o14x.alpha.util.Messages;

/**
 * The FieldEditor to manage columns to be displayed in the FolderView.
 * 
 * @author Olivier DANGREAUX
 */
public class ColumnFieldEditor extends FieldEditor {
	/**
	 * UIController.
	 */
	private UIController uiController;

	/**
	 * CheckboxTableViewer to choose and order columns.
	 */
	private CheckboxTableViewer checkboxTableViewer;

	/**
	 * The button box containing the Up, and Down buttons;
	 */
	private Composite buttonBox;

	/**
	 * The Up button.
	 */
	private Button upButton;

	/**
	 * The Down button.
	 */
	private Button downButton;

	/**
	 * The selection listener.
	 */
	private SelectionListener selectionListener;

	/**
	 * Creates a ColumnFieldEditor
	 * 
	 * @param name the name of the preference this field editor works on
	 * @param labelText the label text of the field editor
	 * @param parent the parent of the field editor's control
	 * @param uiController The UIController
	 */
	protected ColumnFieldEditor(String name, String labelText, Composite parent, UIController uiController) {
		init(name, labelText);
		createControl(parent);
		this.uiController = uiController;
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	protected void adjustForNumColumns(int numColumns) {
		Control control = getLabelControl();
		((GridData) control.getLayoutData()).horizontalSpan = numColumns;
		((GridData) checkboxTableViewer.getTable().getLayoutData()).horizontalSpan = numColumns - 1;
	}

	/**
	 * Creates the Up, and Down button in the given button box.
	 *
	 * @param box the box for the buttons
	 */
	private void createButtons(Composite box) {
		upButton = createPushButton(box, Messages.getString("ColumnFieldEditor.up")); //$NON-NLS-1$
		downButton = createPushButton(box, Messages.getString("ColumnFieldEditor.down")); //$NON-NLS-1$
	}

	/**
	 * Helper method to create a push button.
	 * 
	 * @param parent the parent control
	 * @param key the resource name used to supply the button's label text
	 * @return Button
	 */
	private Button createPushButton(Composite parent, String key) {
		Button button = new Button(parent, SWT.PUSH);
		button.setText(key);
		button.setFont(parent.getFont());
		GridData data = new GridData(GridData.FILL_HORIZONTAL);
		int widthHint = convertHorizontalDLUsToPixels(button, IDialogConstants.BUTTON_WIDTH);
		data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
		button.setLayoutData(data);
		button.addSelectionListener(getSelectionListener());
		return button;
	}

	/**
	 * Creates a selection listener.
	 */
	public void createSelectionListener() {
		selectionListener = new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				Widget widget = event.widget;
				if (widget == upButton) {
					upPressed();
				} else if (widget == downButton) {
					downPressed();
				} else if (widget == checkboxTableViewer.getTable()) {
					selectionChanged();
				}
			}
		};
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	protected void doFillIntoGrid(Composite parent, int numColumns) {
		Control control = getLabelControl(parent);
		GridData gd = new GridData();
		gd.horizontalSpan = numColumns;
		control.setLayoutData(gd);

		Table table = getCheckboxTableViewer(parent).getTable();
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.verticalAlignment = GridData.FILL;
		gd.horizontalSpan = numColumns - 1;
		gd.grabExcessHorizontalSpace = true;
		table.setLayoutData(gd);

		buttonBox = getButtonBoxControl(parent);
		gd = new GridData();
		gd.verticalAlignment = GridData.BEGINNING;
		buttonBox.setLayoutData(gd);
	}

	/**
	 * Loads the columns list from a string.
	 * 
	 * @param value The string containinf the column list and its state.
	 * 
	 * @return The ColumnHelper list.
	 */
	private List<ColumnHelper> loadFromString(String value) {
		List<ColumnHelper> list = null;

		if (value != null) {
			StringTokenizer stringTokenizer1 = new StringTokenizer(value, ";"); //$NON-NLS-1$
			list = new ArrayList<ColumnHelper>();
			while (stringTokenizer1.hasMoreTokens()) {
				String token = stringTokenizer1.nextToken();
				if (token != null && token.trim().length() != 0) {
					StringTokenizer stringTokenizer2 = new StringTokenizer(token, ":"); //$NON-NLS-1$
					String id = stringTokenizer2.nextToken();

					try {
						ColumnHelper columnHelper = (ColumnHelper) Class.forName(id).newInstance();
						columnHelper.setUiController(uiController);
						columnHelper.setEnabled(stringTokenizer2.nextToken().equals("1")); //$NON-NLS-1$

						list.add(columnHelper);
					} catch (InstantiationException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (ClassNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
		return list;
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	protected void doLoad() {
		String s = getPreferenceStore().getString(getPreferenceName());
		if (s == null || s.trim().length() == 0) {
			doLoadDefault();
		} else {
			List<ColumnHelper> columnHelpers = loadFromString(s);
			uiController.getUi().getColumnHelperManager().setColumnHelperList(columnHelpers);
			checkboxTableViewer.setInput(columnHelpers);
		}
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	protected void doLoadDefault() {
		checkboxTableViewer.setInput(uiController.getUi().getColumnHelperManager().getColumnHelperList());
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	@SuppressWarnings("unchecked")
	protected void doStore() {
		StringBuffer s = new StringBuffer();
		updateCheckedState();
		List<ColumnHelper> input = (List<ColumnHelper>) checkboxTableViewer.getInput();
		for (ColumnHelper columnHelper : input) {
			s.append(columnHelper.getClass().getName());
			s.append(":"); //$NON-NLS-1$
			s.append(columnHelper.isEnabled() ? 1 : 0);
			s.append(";"); //$NON-NLS-1$
		}

		String oldValue = getPreferenceStore().getString(getPreferenceName());
		String newValue = s.toString();
		if (!newValue.equals(oldValue)) {
			getPreferenceStore().setValue(getPreferenceName(), s.toString());
			for(ExplorerTab explorerTab : uiController.getDomain().getExplorerTabManager().getExplorerTabMap().values()) {
				explorerTab.getFolderView().initColumns();
			}
			
			uiController.getDomain().getCurrentExplorerTab().getFolderView().update();
		}

	}

	/**
	 * Notifies that the Down button has been pressed.
	 */
	private void downPressed() {
		swap(false);
	}

	/**
	 * Returns this field editor's button box containing the Up, and Down button.
	 *
	 * @param parent the parent control
	 * @return the button box
	 */
	public Composite getButtonBoxControl(Composite parent) {
		if (buttonBox == null) {
			buttonBox = new Composite(parent, SWT.NULL);
			GridLayout layout = new GridLayout();
			layout.marginWidth = 0;
			buttonBox.setLayout(layout);
			createButtons(buttonBox);
			buttonBox.addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					upButton = null;
					downButton = null;
					buttonBox = null;
				}
			});

		} else {
			checkParent(buttonBox, parent);
		}

		selectionChanged();
		return buttonBox;
	}

	/**
	 * Returns this CheckboxTableViewer.
	 *
	 * @param parent the parent control
	 * @return the list control
	 */
	public CheckboxTableViewer getCheckboxTableViewer(Composite parent) {
		if (checkboxTableViewer == null) {
			checkboxTableViewer = CheckboxTableViewer.newCheckList(parent, SWT.FULL_SELECTION | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
			checkboxTableViewer.getTable().setFont(parent.getFont());
			checkboxTableViewer.getTable().addSelectionListener(getSelectionListener());
			checkboxTableViewer.getTable().addDisposeListener(new DisposeListener() {
				public void widgetDisposed(DisposeEvent event) {
					checkboxTableViewer = null;
				}
			});

			TableColumn tableColumn = new TableColumn(checkboxTableViewer.getTable(), SWT.NONE);
			tableColumn.setWidth(300);
			tableColumn.setText(Messages.getString("ColumnFieldEditor.column")); //$NON-NLS-1$

			checkboxTableViewer.getTable().setHeaderVisible(true);
			checkboxTableViewer.getTable().setLinesVisible(true);

			checkboxTableViewer.setContentProvider(new IStructuredContentProvider() {

				@Override
				public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
					// TODO Auto-generated method stub

				}

				@Override
				public void dispose() {
					// TODO Auto-generated method stub

				}

				@SuppressWarnings("unchecked")
				@Override
				public Object[] getElements(Object inputElement) {
					List<ColumnHelper> columnHelperList = (List<ColumnHelper>) inputElement;
					return columnHelperList.toArray();
				}
			});

			checkboxTableViewer.setLabelProvider(new ITableLabelProvider() {

				@Override
				public void removeListener(ILabelProviderListener listener) {
					// TODO Auto-generated method stub

				}

				@Override
				public boolean isLabelProperty(Object element, String property) {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public void dispose() {
					// TODO Auto-generated method stub

				}

				@Override
				public void addListener(ILabelProviderListener listener) {
					// TODO Auto-generated method stub

				}

				@Override
				public String getColumnText(Object element, int columnIndex) {
					String text = ""; //$NON-NLS-1$
					if (columnIndex == 0) {
						text = ((ColumnHelper) element).getTitle();
					}
					return text;
				}

				@Override
				public Image getColumnImage(Object element, int columnIndex) {
					// TODO Auto-generated method stub
					return null;
				}
			});

			checkboxTableViewer.setCheckStateProvider(new ICheckStateProvider() {

				@Override
				public boolean isGrayed(Object element) {
					// TODO Auto-generated method stub
					return false;
				}

				@Override
				public boolean isChecked(Object element) {
					// TODO Auto-generated method stub
					return ((ColumnHelper) element).isEnabled();
				}
			});

		} else {
			checkParent(checkboxTableViewer.getTable(), parent);
		}
		return checkboxTableViewer;
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	public int getNumberOfControls() {
		return 2;
	}

	/**
	 * Returns this field editor's selection listener.
	 * The listener is created if nessessary.
	 *
	 * @return the selection listener
	 */
	private SelectionListener getSelectionListener() {
		if (selectionListener == null) {
			createSelectionListener();
		}
		return selectionListener;
	}

	/**
	 * Returns this field editor's shell.
	 * <p>
	 * This method is internal to the framework; subclassers should not call
	 * this method.
	 * </p>
	 *
	 * @return the shell
	 */
	protected Shell getShell() {
		if (upButton == null) {
			return null;
		}
		return upButton.getShell();
	}

	/**
	 * Invoked when the selection in the list has changed.
	 * 
	 * <p>
	 * The default implementation of this method utilizes the selection index
	 * and the size of the list to toggle the enablement of the up, down and
	 * remove buttons.
	 * </p>
	 * 
	 * <p>
	 * Sublcasses may override.
	 * </p>
	 * 
	 * @since 3.5
	 */
	protected void selectionChanged() {

		int index = checkboxTableViewer.getTable().getSelectionIndex();
		int size = checkboxTableViewer.getTable().getItemCount();

		upButton.setEnabled(size > 1 && index > 0);
		downButton.setEnabled(size > 1 && index >= 0 && index < size - 1);
	}

	/* (non-Javadoc)
	 * Method declared on FieldEditor.
	 */
	public void setFocus() {
		if (checkboxTableViewer.getTable() != null) {
			checkboxTableViewer.getTable().setFocus();
		}
	}

	/**
	 * Moves the currently selected item up or down.
	 *
	 * @param up <code>true</code> if the item should move up,
	 *  and <code>false</code> if it should move down
	 */
	@SuppressWarnings("unchecked")
	private void swap(boolean up) {
		setPresentsDefaultValue(false);
		int index = checkboxTableViewer.getTable().getSelectionIndex();
		int target = up ? index - 1 : index + 1;

		if (index >= 0) {
			updateCheckedState();
			ColumnHelper selection = (ColumnHelper) ((IStructuredSelection) checkboxTableViewer.getSelection()).getFirstElement();
			List<ColumnHelper> input = (List<ColumnHelper>) checkboxTableViewer.getInput();
			input.remove(index);
			input.add(target, selection);

			checkboxTableViewer.setInput(input);
			checkboxTableViewer.setSelection(new StructuredSelection(selection));

		}
		selectionChanged();
	}

	/**
	 * Updates the checked state in the underlying list af columns.
	 */
	@SuppressWarnings("unchecked")
	private void updateCheckedState() {
		List<ColumnHelper> input = (List<ColumnHelper>) checkboxTableViewer.getInput();
		for (ColumnHelper column : input) {
			column.setEnabled(checkboxTableViewer.getChecked(column));
		}
	}

	/**
	 * Notifies that the Up button has been pressed.
	 */
	private void upPressed() {
		swap(true);
	}

	/*
	 * @see FieldEditor.setEnabled(boolean,Composite).
	 */
	public void setEnabled(boolean enabled, Composite parent) {
		super.setEnabled(enabled, parent);
		getCheckboxTableViewer(parent).getTable().setEnabled(enabled);
		//        addButton.setEnabled(enabled);
		//        removeButton.setEnabled(enabled);
		upButton.setEnabled(enabled);
		downButton.setEnabled(enabled);
	}

	/**
	 * Return the Up button.  
	 * 
	 * @return the button
	 * @since 3.5
	 */
	protected Button getUpButton() {
		return upButton;
	}

	/**
	 * Return the Down button.  
	 * 
	 * @return the button
	 * @since 3.5
	 */
	protected Button getDownButton() {
		return downButton;
	}

	/**
	 * Returns the value of uiController.
	 *
	 * @return The value of uiController.
	 */
	public UIController getUiController() {
		return uiController;
	}

	/**
	 * Sets the value of uiController.
	 *
	 * @param uiController The value of uiController to set.
	 */
	public void setUiController(UIController uiController) {
		this.uiController = uiController;
	}
}
